package util

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/google/wire"
	"go.manyji.com/lib/log"
	"go.manyji.com/lib/mysql"
)

var TransSet = wire.NewSet(wire.Struct(new(Trans), "*"))

type Trans struct {
	DB *mysql.DB
}

func (a *Trans) Exec(ctx context.Context, fn func(ctx context.Context, t *mysql.Tx) error) error {

	return a.DB.Transaction(func(t *mysql.Tx) error {
		ctx = context.WithValue(ctx, "transaction", t.Tx)

		return fn(ctx, t)
	})
}

func (a *Trans) TransactionTx(ctx context.Context, fn func(ctx context.Context, t *mysql.Tx) error, args ...interface{}) error {

	var opts *sql.TxOptions
	if len(args) == 1 {
		opts = args[0].(*sql.TxOptions)
	}

	tx, err := a.DB.BeginTx(context.Background(), opts)
	if err != nil {
		return err
	}

	defer func() {
		if r := recover(); r != nil {
			_ = tx.Rollback()
			log.Err(fmt.Sprintf("transaction aborted due to %v", r))
		}
	}()

	ctx = context.WithValue(ctx, "transaction", tx)
	if err := fn(ctx, &mysql.Tx{Tx: tx}); err != nil {
		_ = tx.Rollback()
		return err
	}

	return tx.Commit()
}
